﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Diagnostics;
using System.Web.UI.WebControls;
using System.IO;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Collections.ObjectModel;
using System.Collections;
using Microsoft.SharePoint.Utilities;
using System.Web;
using System.Web.Services;
using System.Web.Script.Services;

namespace PowerShellConsole.PageCode 
{
    public class PSConsole : LayoutsPageBase 
    {
        const string PSC_SavedKey = "PSC_SavedScripts";
        const string PSC_RecentKey = "PSC_RecentScripts";
        protected TextBox txtCommand;
        protected Literal lblOutput;
        protected Label lblError;
        protected DropDownList ddlSavedScripts;
        protected DropDownList ddlRecentScripts;
        protected FileUpload uplScript;

        private static string RunspaceKey
        {
            get { return "runspace_" + HttpContext.Current.User.Identity.Name; }
        }

        private static Runspace RunspaceInstance
        {
            get
            {
                Runspace runspace = HttpContext.Current.Application[RunspaceKey] as Runspace;
                if (runspace == null)
                {
                    runspace = RunspaceFactory.CreateRunspace();
                    runspace.Open();
                    HttpContext.Current.Application[RunspaceKey] = runspace;
                }
                return runspace;
            }
        }

        protected override void OnLoad(EventArgs e) {
            base.OnLoad(e);

            if (!SPContext.Current.Web.UserIsWebAdmin)
            {
                SPUtility.TransferToErrorPage("Access denied.");
            }
            if (!IsPostBack)
            {
                SPList scriptsList = SPContext.Current.Site.RootWeb.Lists["PowershellScripts"];
                ddlSavedScripts.Items.Clear();
                foreach (SPListItem item in scriptsList.Items)
                {
                    ddlSavedScripts.Items.Add(new ListItem(item.Name, item.ID.ToString()));
                }
                ddlRecentScripts.DataSource = (List<string>)Application[PSC_RecentKey];
                ddlRecentScripts.DataBind();

                if (!string.IsNullOrEmpty(Request.QueryString["ID"]) && Request.QueryString["cmd"] == "run")
                {
                    SPFile scriptFile = scriptsList.GetItemById(Convert.ToInt32(Request.QueryString["ID"])).File;
                    txtCommand.Text = ASCIIEncoding.ASCII.GetString(scriptFile.OpenBinary());

                    btnRun_OnClick(null, null);
                }
            }
        }

        protected void buttonClose_OnClick(object sender, EventArgs e) {
            Response.Redirect("../settings.aspx");
        }

        protected void btnRun_OnClick(object sender, EventArgs e) {
            lblOutput.Text = string.Empty;
            lblError.Text = string.Empty;
            SPSecurity.RunWithElevatedPrivileges(delegate {
                try
                {
                    RunspaceInvoke invoke = new RunspaceInvoke();
                    IList errors = new List<string>();
                    Collection<PSObject> results = invoke.Invoke(txtCommand.Text, new List<string>(), out errors);

                    foreach (PSObject result in results)
                    {
                        lblOutput.Text += Convert.ToString(result).Replace("\r\n", "<br/>").Replace(" ", "&nbsp;") + "<br/>";
                    }
                    foreach (object error in errors)
                    {
                        lblError.Text += Convert.ToString(error).Replace("\r\n", "<br/>").Replace(" ", "&nbsp;") + "<br/>";
                    }
                }
                catch (Exception ex)
                {
                    lblError.Text = ex.ToString().Replace("\r\n", "<br/>").Replace(" ", "&nbsp;") + "<br/>";
                }

                List<string> pscScripts = null;
                if (Application[PSC_RecentKey] == null)
                {
                    pscScripts = new List<string>();
                    Application[PSC_RecentKey] = pscScripts;
                }
                else
                {
                    pscScripts = (List<string>)Application[PSC_RecentKey];
                }
                if (pscScripts.Contains(txtCommand.Text))
                {
                    pscScripts.Remove(txtCommand.Text);
                }
                pscScripts.Insert(0, txtCommand.Text);
                ddlRecentScripts.DataSource = pscScripts;
                ddlRecentScripts.DataBind();
            });
        }

        protected void btnSave_OnClick(object sender, EventArgs e)
        {
            SPList scriptsList = SPContext.Current.Site.RootWeb.Lists["PowershellScripts"];
            SPFile scriptFile = scriptsList.RootFolder.Files.Add(Guid.NewGuid().ToString() + ".ps1", ASCIIEncoding.ASCII.GetBytes(txtCommand.Text));
            Response.Redirect(scriptsList.Forms[PAGETYPE.PAGE_EDITFORM].ServerRelativeUrl + "?Source=" + Request.Path + "&ID=" + scriptFile.Item.ID);
        }

        protected void ddlSavedScripts_SelectedIndexChanged(object sender, EventArgs e)
        {
            SPList scriptsList = SPContext.Current.Site.RootWeb.Lists["PowershellScripts"];
            SPFile scriptFile = scriptsList.GetItemById(Convert.ToInt32(ddlSavedScripts.SelectedValue)).File;
            txtCommand.Text = ASCIIEncoding.ASCII.GetString(scriptFile.OpenBinary());
        }

        protected void ddlRecentScripts_SelectedIndexChanged(object sender, EventArgs e)
        {
            txtCommand.Text = ddlRecentScripts.SelectedValue;
        }

        protected void btnUpload_OnClick(object sender, EventArgs e)
        {
            txtCommand.Text = ASCIIEncoding.ASCII.GetString(uplScript.FileBytes);
            btnRun_OnClick(null, null);
        }

        protected void btnOpenScriptLibrary_OnClick(object sender, EventArgs e)
        {
            SPList scriptsList = SPContext.Current.Site.RootWeb.Lists["PowershellScripts"];
            Response.Redirect(scriptsList.DefaultView.ServerRelativeUrl);
        }

        [WebMethod, ScriptMethod]
        public static string Execute(string line)
        {
            StringBuilder output = new StringBuilder();
            try
            {
                Runspace runspace = RunspaceInstance;
                Pipeline pipeline = runspace.CreatePipeline(line);
                pipeline.Commands.Add("out-string");
                IList errors = new List<string>();

                Collection<PSObject> results = pipeline.Invoke();

                foreach (PSObject result in results)
                {
                    output.Append(Convert.ToString(result).Replace("\r\n", "<br/>").Replace(" ", "&nbsp;") + "<br/>");
                }
                foreach (object error in pipeline.Error.ReadToEnd())
                {
                    output.Append(Convert.ToString(error).Replace("\r\n", "<br/>").Replace(" ", "&nbsp;") + "<br/>");
                }
            }
            catch (Exception ex)
            {
                output.Append(ex.ToString().Replace("\r\n", "<br/>").Replace(" ", "&nbsp;") + "<br/>");
            }
            return output.ToString();
        }
    }
}
